﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace Wulong.Phone.Utils.HttpHelpers
{
    public static class PostHelper
    {
        public static byte[] GetMultipartFormData(Dictionary<string, object> postParameters, string boundary)
        {
            Encoding encoding = Encoding.UTF8;  
            Stream formDataStream = new MemoryStream();

            foreach (var param in postParameters)
            {
                if (param.Value is FileParameter)
                {
                    var fileToUpload = (FileParameter)param.Value;

                    // Add just the first part of this param, since we will write the file data directly to the Stream
                    string header = 
                        string.Format("\r\n--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n",
                        boundary,
                        param.Key,
                        fileToUpload.FileName ?? param.Key,
                        fileToUpload.ContentType ?? "application/octet-stream");

                    formDataStream.Write(encoding.GetBytes(header), 0, header.Length);

                    // Write the file data directly to the Stream, rather than serializing it to a string.
                    formDataStream.Write(fileToUpload.File, 0, fileToUpload.File.Length);
                    // Thanks to feedback from commenters, add a CRLF to allow multiple files to be uploaded
                    formDataStream.Write(encoding.GetBytes("\r\n"), 0, 2);
                }
                else
                {
                    var postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n",
                        boundary,
                        param.Key,
                        param.Value);
                    formDataStream.Write(encoding.GetBytes(postData), 0, postData.Length);
                }
            }

            // Add the end of the request
            string footer = "\r\n--" + boundary + "--\r\n";
            formDataStream.Write(encoding.GetBytes(footer), 0, footer.Length);

            // Dump the Stream into a byte[]
            formDataStream.Position = 0;
            var formData = new byte[formDataStream.Length];
            formDataStream.Read(formData, 0, formData.Length);
            formDataStream.Close();

            return formData;
        }

        public static byte[] GetMultipartFormData(string boundary, FileParameter fileParam, string contentType, Dictionary<string, object> nvc)
        {
            Stream formDataStream = new MemoryStream();

            byte[] boundarybytes = Encoding.UTF8.GetBytes("\r\n--" + boundary + "\r\n"); 

            string formdataTemplate = "Content-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}"; 
            foreach (string key in nvc.Keys)
            {
                formDataStream.Write(boundarybytes, 0, boundarybytes.Length); 
                string formitem = string.Format(formdataTemplate, key, nvc[key]); 
                byte[] formitembytes = Encoding.UTF8.GetBytes(formitem);
                formDataStream.Write(formitembytes, 0, formitembytes.Length);
            }
            formDataStream.Write(boundarybytes, 0, boundarybytes.Length); 
            string headerTemplate = "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n";
            string header = string.Format(headerTemplate, "article[pciture]", fileParam==null?"":fileParam.FileName, contentType); 
            byte[] headerbytes = Encoding.UTF8.GetBytes(header);
            formDataStream.Write(headerbytes, 0, headerbytes.Length); 

            if (fileParam!=null)
            {
                formDataStream.Write(fileParam.File, 0, fileParam.File.Length);
            } 

            byte[] trailer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
            formDataStream.Write(trailer, 0, trailer.Length);
            var formData = new byte[formDataStream.Length];
            formDataStream.Read(formData, 0, formData.Length);
            formDataStream.Close();

            return formData;
        }

        public static byte[] ReadToEnd(this Stream stream)
        {
            long originalPosition = stream.Position;
            stream.Position = 0;
            try
            {
                var readBuffer = new byte[4096];
                int totalBytesRead = 0;
                int bytesRead;
                while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
                {
                    totalBytesRead += bytesRead;
                    if (totalBytesRead == readBuffer.Length)
                    {
                        var nextByte = stream.ReadByte();
                        if (nextByte == -1) continue;
                        var temp = new byte[readBuffer.Length * 2];
                        Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
                        Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
                        readBuffer = temp;
                        totalBytesRead++;
                    }
                }
                var buffer = readBuffer;
                if (readBuffer.Length != totalBytesRead)
                {
                    buffer = new byte[totalBytesRead];
                    Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
                }
                return buffer;
            }
            finally
            {
                stream.Position = originalPosition;
            }
        }
    }
}
